package main

import (
	"context"
	"fmt"
	"strings"

	"gitee.com/u-language/u-language/ucom/ast"
	"gitee.com/u-language/u-language/ucom/cast"
	"gitee.com/u-language/u-language/ucom/check"
	"gitee.com/u-language/u-language/ucom/config"
	"gitee.com/u-language/u-language/ucom/data"
	"gitee.com/u-language/u-language/ucom/errcode"
	. "gitee.com/u-language/u-language/ucom/internal/parser"
	"gitee.com/u-language/u-language/ucom/internal/utils"
	"gitee.com/u-language/u-language/ucom/parser"
)

const Thread2 = true

// 改编自main包的BenchmarkParserStr
func BenchmarkParserStr() {
	type args struct {
		str    []string
		Thread bool
	}
	tests := []struct {
		name string
		args args
	}{
		{"多个变量声明并赋值", args{str: reta1(), Thread: Thread2}},
		{"多个goto", args{str: reta2(), Thread: Thread2}},
		{"多个malloc", args{str: reta3(), Thread: Thread2}},
		{"多个if", args{str: reta4(), Thread: Thread2}},
		{"多个3子句for", args{str: reta5(), Thread: Thread2}},
		{"多个1字段结构体", args{str: reta6(), Thread: Thread2}},
		{"多个选择器x.y", args{str: reta7(), Thread: Thread2}},
		{"多个mallocSize", args{str: reta8(), Thread: Thread2}},
		{"多个枚举", args{str: reta9(), Thread: Thread2}},
		{"多个break和continue", args{str: reta10(), Thread: Thread2}},
		{"多个常量声明并赋值", args{str: reta11(), Thread: Thread2}},
		{"多个多行注释", args{str: reta12(), Thread: Thread2}},
	}
	wg := utils.NewWaitGroup(Thread2)
	for _, tt := range tests {
		tt := tt
		wg.Go(func() {
			defer wg.Done()
			tree := ParserStr(tt.args.str)
			check.CheckTree(tree, errcode.DefaultErrCtx, false)
			toc := cast.NewUtoC()
			toc.Parser(tree, false)
			toc.C()
		})
	}
	wg.Wait()
}

func main() {
	BenchmarkParserStr()
	BenchmarkComplierToC()
	TestCheckTree()
	TestCheckSyntaxErr()
}

const n = 150

func reta1() []string {
	ret, ok := a1.Get()
	if ok {
		return ret
	}
	ret = make([]string, n+3)
	ret[0] = "package main"
	ret[1] = "func main(){"
	for i := 2; i < n; i++ {
		ret[i] = fmt.Sprint("var ", "a", i, " int=1")
	}
	ret[len(ret)-1] = "}"
	a1.Set(ret)
	return ret
}

func reta2() []string {
	ret, ok := a2.Get()
	if ok {
		return ret
	}
	ret = make([]string, n+4)
	ret[0] = "package main"
	ret[1] = "func main(){"
	ret[2] = "s:"
	for i := 3; i < n; i++ {
		ret[i] = "goto s"
	}
	ret[len(ret)-1] = "}"
	a2.Set(ret)
	return ret
}

func reta3() []string {
	ret, ok := a3.Get()
	if ok {
		return ret
	}
	ret = make([]string, n+3)
	ret[0] = "package main"
	ret[1] = "func main(){"
	for i := 2; i < n; i++ {
		ret[i] = "malloc(int)"
	}
	ret[len(ret)-1] = "}"
	a3.Set(ret)
	return ret
}

func reta4() []string {
	ret, ok := a4.Get()
	if ok {
		return ret
	}
	ret = make([]string, n+3)
	ret[0] = "package main"
	ret[1] = "func main(){"
	for i := 2; i+1 < n; i += 2 {
		ret[i] = "if true==true{"
		ret[i+1] = "}"
	}
	ret[len(ret)-1] = "}"
	a4.Set(ret)
	return ret
}

func reta5() []string {
	ret, ok := a5.Get()
	if ok {
		return ret
	}
	ret = make([]string, n+3)
	ret[0] = "package main"
	ret[1] = "func main(){"
	for i := 3; i+1 < n; i += 2 {
		ret[i] = "for var i int;i<100;i++{"
		ret[i+1] = "}"
	}
	ret[len(ret)-1] = "}"
	a5.Set(ret)
	return ret
}

func reta6() []string {
	ret, ok := a6.Get()
	if ok {
		return ret
	}
	ret = make([]string, n+3)
	ret[0] = "package main"
	ret[1] = "func main(){"
	for i := 2; i+2 < n; i += 3 {
		ret[i] = fmt.Sprint("struct a", i, "{")
		ret[i+1] = "a int"
		ret[i+2] = "}"
	}
	ret[len(ret)-1] = "}"
	a6.Set(ret)
	return ret
}

func reta7() []string {
	ret, ok := a7.Get()
	if ok {
		return ret
	}
	ret = make([]string, n+6)
	ret[0] = "package main"
	ret[1] = "func main(){"
	ret[2] = "struct a{"
	ret[3] = "len int"
	ret[4] = "}"
	ret[5] = "var s a"
	for i := 6; i < n; i++ {
		ret[i] = "s.len=1"
	}
	ret[len(ret)-1] = "}"
	a7.Set(ret)
	return ret
}

func reta8() []string {
	ret, ok := a8.Get()
	if ok {
		return ret
	}
	ret = make([]string, n+3)
	ret[0] = "package main"
	ret[1] = "func main(){"
	for i := 2; i < n; i++ {
		ret[i] = "mallocSize(8)"
	}
	ret[len(ret)-1] = "}"
	a8.Set(ret)
	return ret
}

func reta9() []string {
	ret, ok := a9.Get()
	if ok {
		return ret
	}
	ret = make([]string, n+3)
	ret[0] = "package main"
	ret[1] = "func main(){"
	for i := 2; i+2 < n; i += 3 {
		ret[i] = fmt.Sprintf("enum s%d {", i)
		ret[i+1] = "d"
		ret[i+2] = "}"
	}
	ret[len(ret)-1] = "}"
	a9.Set(ret)
	return ret
}

func reta10() []string {
	ret, ok := a10.Get()
	if ok {
		return ret
	}
	ret = make([]string, n+4)
	ret[0] = "package main"
	ret[1] = "func main(){"
	ret[2] = "var i int=1"
	for i := 3; i+7 < n; i += 8 {
		ret[i] = "for ;; {"
		ret[i+1] = "if i==1{"
		ret[i+2] = "break"
		ret[i+3] = "}"
		ret[i+4] = "else{"
		ret[i+5] = "continue"
		ret[i+6] = "}"
		ret[i+7] = "}"
	}
	ret[len(ret)-1] = "}"
	a10.Set(ret)
	return ret
}

func reta11() []string {
	ret, ok := a11.Get()
	if ok {
		return ret
	}
	ret = make([]string, n+3)
	ret[0] = "package main"
	ret[1] = "func main(){"
	for i := 2; i < n; i++ {
		ret[i] = fmt.Sprintf("const s%d int=0", i)
	}
	ret[len(ret)-1] = "}"
	a11.Set(ret)
	return ret
}

func reta12() []string {
	ret, ok := a12.Get()
	if ok {
		return ret
	}
	ret = make([]string, n+3)
	ret[0] = "package main"
	ret[1] = "func main(){"
	for i := 2; i+2 < n; i += 3 {
		ret[i] = "/*"
		ret[i+1] = ""
		ret[i+2] = "*/"
	}
	ret[len(ret)-1] = "}"
	a12.Set(ret)
	return ret
}

var (
	a1  data.OnceValue[[]string]
	a2  data.OnceValue[[]string]
	a3  data.OnceValue[[]string]
	a4  data.OnceValue[[]string]
	a5  data.OnceValue[[]string]
	a6  data.OnceValue[[]string]
	a7  data.OnceValue[[]string]
	a8  data.OnceValue[[]string]
	a9  data.OnceValue[[]string]
	a10 data.OnceValue[[]string]
	a11 data.OnceValue[[]string]
	a12 data.OnceValue[[]string]
)

func init() {
	go errcode.Handle(context.Background(), func(err errcode.ErrInfo) { panic(err.String()) }, func() {})
	errcode.Debug = true
	utils.Debug = true
	config.PrintTime = "off"
}

// 改编自main包的 BenchmarkComplierToC
func BenchmarkComplierToC() {
	type args struct {
		path      string
		cfilename string
		outname   string
	}
	a1 := args{path: "../testdata/5.txt", cfilename: "5.c", outname: "5" + utils.Ext}
	a2 := args{path: "../testdata/6.txt", cfilename: "6.c", outname: "6" + utils.Ext}
	a3 := args{path: "../testdata/7.txt", cfilename: "7.c", outname: "7" + utils.Ext}
	a4 := args{path: "../testdata/8.txt", cfilename: "8.c", outname: "8" + utils.Ext}
	a5 := args{path: "../testdata/9.txt", cfilename: "9.c", outname: "9" + utils.Ext}
	a6 := args{path: "../testdata/10.txt", cfilename: "10.c", outname: "10" + utils.Ext}
	a7 := args{path: "../testdata/11.txt", cfilename: "11.c", outname: "11" + utils.Ext}
	a8 := args{path: "../testdata/12.txt", cfilename: "12.c", outname: "12" + utils.Ext}
	a9 := args{path: "../testdata/13.txt", cfilename: "13.c", outname: "13" + utils.Ext}
	a10 := args{path: "../testdata/14.txt", cfilename: "14.c", outname: "14" + utils.Ext}
	a11 := args{path: "../testdata/15.txt", cfilename: "15.c", outname: "15" + utils.Ext}
	a12 := args{path: "../testdata/16.txt", cfilename: "16.c", outname: "16" + utils.Ext}
	a13 := args{path: "../testdata/u17", cfilename: "./u17", outname: "17" + utils.Ext}
	a14 := args{path: "../testdata/18.txt", cfilename: "18.c", outname: "18" + utils.Ext}
	a15 := args{path: "../testdata/19.txt", cfilename: "19.c", outname: "19" + utils.Ext}
	a16 := args{path: "../testdata/20.txt", cfilename: "20.c", outname: "20" + utils.Ext}
	a17 := args{path: "../testdata/21.txt", cfilename: "21.c", outname: "21" + utils.Ext}
	a18 := args{path: "../testdata/u22", cfilename: "./u22", outname: "22" + utils.Ext}
	a19 := args{path: "../testdata/23.txt", cfilename: "u23.c", outname: "23" + utils.Ext}
	a20 := args{path: "../testdata/u24", cfilename: "./u24", outname: "24" + utils.Ext}
	a21 := args{path: "../testdata/u24/main.u", cfilename: "./u24u.c", outname: "24u" + utils.Ext}
	a22 := args{path: "../testdata/25.u", cfilename: "25.c", outname: "25" + utils.Ext}
	a23 := args{path: "../testdata/26.u", cfilename: "26.c", outname: "26" + utils.Ext}
	a24 := args{path: "../testdata/u27/main.u", cfilename: "27.c", outname: "27" + utils.Ext}
	a25 := args{path: "../testdata/u27", cfilename: "./u27u.c", outname: "27u" + utils.Ext}
	a26 := args{path: "../testdata/u28", cfilename: "./u28u.c", outname: "28u" + utils.Ext}
	a27 := args{path: "../testdata/u28/main.u", cfilename: "./27.c", outname: "28" + utils.Ext}
	a28 := args{path: "../testdata/u29/main.u", cfilename: "./29.c", outname: "29" + utils.Ext}
	a29 := args{path: "../testdata/u29", cfilename: "./29u.c", outname: "29u" + utils.Ext}
	tests := []struct {
		name string
		args args
		want string
	}{
		{name: "输出456", args: a1, want: "456"},
		{name: "for加1，1万次，输出10000", args: a2, want: "10000"},
		{name: "for代码块和main代码块都有变量i，输出10000", args: a3, want: "10000"},
		{name: "输出1-10000中偶数的数量,比较\"s\"==\"a\"输出false", args: a4, want: "5000\tfalse"},
		{name: "使用指针循环加一个int变量自增10000次", args: a5, want: "10000"},
		{name: "输出结构体字段，结果字符串11", args: a6, want: "11"},
		{name: "整数和浮点数互相转换", args: a7, want: "1 1.000000"},
		{name: "调用malloc和free", args: a8, want: "1"},
		{name: "使用自操作语句", args: a9, want: "110"},
		{name: "使用nil", args: a10, want: "1nilnil"},
		{name: "使用选择器取地址与解引用", args: a11, want: "1"},
		{name: "使用init函数", args: a12, want: "1"},
		{name: "多文件", args: a13, want: "1101nilnil11"},
		{name: "switch", args: a14, want: ">=3>=2>=1!=3,2,1,0"},
		{name: "使用方法", args: a15, want: "1"},
		{name: "使用指针运算和指针类型转换", args: a16, want: "1"},
		{name: "使用自动释放块", args: a17, want: "1"},
		{name: "多文件使用自动释放块", args: a18, want: "0"},
		{name: "位与，位或，异或，逻辑运算,括号表达式", args: a19, want: "011truetruetrue"},
		{name: "包导入包", args: a20, want: "42"},
		{name: "包导入包(文件模式)", args: a21, want: "42"},
		{name: "枚举", args: a22, want: "o1"},
		{name: "数组类型与索引表达式", args: a23, want: "222"},
		{name: "泛型", args: a24, want: "1 1.2"},
		{name: "泛型(目录模式)", args: a25, want: "1 1.2"},
		{name: "多重导入", args: a26, want: "44"},
		{name: "多重导入(文件模式)", args: a27, want: "44"},
		{name: "跨包使用自动释放块(文件模式)", args: a28, want: strings.Repeat("9", 10)},
		{name: "跨包使用自动释放块", args: a29, want: strings.Repeat("9", 10)},
	}
	wg := utils.NewWaitGroup(Thread2)
	for _, ttf := range tests {
		tt := ttf
		wg.Go(func() {
			defer wg.Done()
			//解析获得抽象语法树，并进行语义检查
			iface, err := parser.ParserAuto(tt.args.path, Thread2, errcode.DefaultErrCtx)
			utils.MustErr(err)
			if errcode.Errbol() { //代码有错误
				return
			}
			switch iface.(type) {
			case *ast.Tree:
				toc := cast.NewUtoC()
				ast := iface.(*ast.Tree)
				toc.Parser(ast, false) //转换为C抽象语法树
				toc.C()
			case *ast.Package:
				p := cast.NewPackage(Thread2)
				astp := iface.(*ast.Package)
				p.AddUastSlice(astp, nil)
				p.String()
			}
		})
	}
	wg.Wait()
}

// 改编自check包的TestCheckTree
func TestCheckTree() {
	type args struct {
		tree   *ast.Tree
		errctx *errcode.ErrCtx
		cancel context.CancelFunc
	}
	newargs := func(str []string, errf func(errcode.ErrInfo), num int, name string) args {
		actx := errcode.NewErrCtx()
		astdctx, acancel := context.WithCancel(context.Background())
		i := 0
		num = num * 2 //因为会检查两次，所以乘2
		s := make(chan struct{})
		go actx.Handle(astdctx, func(info errcode.ErrInfo) {
			i++
			errf(info)
		}, func() { s <- struct{}{} })
		go func() {
			<-astdctx.Done()
			if num != 0 { //如果预期发生错误数量大于0，等待处理所有错误完毕
				<-s
			}
			if num != i { //如果预期发生错误数量不等于实际发生错误数量
				panic(fmt.Errorf("%s 应该有%d个错误，实际有%d个错误", name, num, i))
			}
		}()
		ret := args{tree: ParserStr(str, actx), errctx: actx, cancel: acancel}
		return ret
	}
	a1 := newargs([]string{"package main", "func main(){", "var b int=1", "var c int=1.0", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 4, errcode.NewMsgAssignTypeIsNotEqual("int", "float"), errcode.TypeIsNotEqual) {
			panic("a1\n" + err.String())
		}
	}, 1, "a1")
	a3 := newargs([]string{"package main", "func main(){", "var b int=1+2+3", "}"}, func(err errcode.ErrInfo) { panic("a3\n" + err.String()) }, 0, "a3")
	a4 := newargs([]string{"package main", "func main(){", "var b int=1+2.0+3", "var c int=1+2*3.0+2", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 3, errcode.NewMsgOpexprTypeIsNotEqual("int", "float"), errcode.TypeIsNotEqual) && !err.IsErr("1.txt", 4, errcode.NewMsgOpexprTypeIsNotEqual("int", "float"), errcode.TypeIsNotEqual) {
			panic("a4\n" + err.String())
		}
	}, 2, "a4")
	a5 := newargs([]string{"package main", "func main(){", "var b int=1.0+2.0+3.0", "b=1.9", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 3, errcode.NewMsgAssignTypeIsNotEqual("int", "float"), errcode.TypeIsNotEqual) && !err.IsErr("1.txt", 4, errcode.NewMsgAssignTypeIsNotEqual("int", "float"), errcode.TypeIsNotEqual) {
			panic("a5\n" + err.String())
		}
	}, 2, "a5")
	a6 := newargs([]string{"package main", "func main(){", "b=1", "var b int", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 3, errcode.NewMsgSymbol("b"), errcode.SymbolUseBeforeDeclaration) {
			panic("a6\n" + err.String())
		}
	}, 1, "a6")
	a7 := newargs([]string{"package main", "func main(){", "var c int=1", "c=c+b", "c=b+c", "var b int", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 4, errcode.NewMsgSymbol("b"), errcode.SymbolUseBeforeDeclaration) && !err.IsErr("1.txt", 5, errcode.NewMsgSymbol("b"), errcode.SymbolUseBeforeDeclaration) {
			panic("a7\n" + err.String())
		}
	}, 2, "a7")
	a8 := newargs([]string{"package main", "func main(){", "var b int=1", "if b==1{", "}", "if b+b{", "}", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 6, nil, errcode.ExprNoBool) {
			panic("a8\n" + err.String())
		}
	}, 1, "a8")
	a9 := newargs([]string{"package main", "func main(){", "var b int=1", "if b==1{", "}", "else if b+b{", "}", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 6, nil, errcode.ExprNoBool) {
			panic("a9\n" + err.String())
		}
	}, 1, "a9")
	a10 := newargs([]string{"package main", "func main(){", "for var i int;i<1;i=i+1{", "}", "var i int", "for i=i+1;;{", "}", "for ;;var j int{", "}", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 6, nil, errcode.ForInitNoDeclVarStmt) && !err.IsErr("1.txt", 8, nil, errcode.ForEndStmt) {
			panic("a10\n" + err.String())
		}
	}, 2, "a10")
	a11 := newargs([]string{"package main", "func main(){", "var i int =a", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 3, errcode.NewMsgUnknownSymbol("a"), errcode.UnknownSymbol) {
			panic("a11\n" + err.String())
		}
	}, 1, "a11")
	a12 := newargs([]string{"package main", "func main(){", "var i bool =true+false", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 3, nil, errcode.BoolOpOnlyCmpOrLogic) {
			panic("a12\n" + err.String())
		}
	}, 1, "a12")
	a13 := newargs([]string{"package main", "func add()int{", "return true", "}", "func main(){", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 3, errcode.NewMsgReturnTypeIsNotEqual("int", "bool"), errcode.ReturnErr) {
			panic("a13\n" + err.String())
		}
	}, 1, "a13")
	a14 := newargs([]string{"package main", "func main(){", "r:", "goto re", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 4, errcode.NewMsgLabelNoExist("re"), errcode.GotoErr) {
			panic("a14\n" + err.String())
		}
	}, 1, "a14")
	a15 := newargs([]string{"package main", "func main(){", "for ;;{", "if true==true{", "break", "}", "}", "break", "continue", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 8, nil, errcode.BreakStmtInForOrSwitch) && !err.IsErr("1.txt", 9, nil, errcode.ContinueStmtInFor) {
			panic("a15\n" + err.String())
		}
	}, 2, "a15")
	a16 := newargs([]string{"package main", "func main(){", "const a int = i ", "a=1", "var i int=1", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 4, errcode.NewMsgAssignToConst("a"), errcode.ASSIGNErr) && !err.IsErr("1.txt", 3, errcode.NewMsgSymbol("i"), errcode.SymbolUseBeforeDeclaration) {
			panic("a16\n" + err.String())
		}
	}, 2, "a16")
	a17 := newargs([]string{"package main", "func main(){", "var a float = float(10) ", "var b int=int(&a)", "var c int=int(a,1)", "var d int=int()", "var e int=int(f)", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 4, nil, errcode.CanOnlyConvertPointerTypeToUnsafePointer) && !err.IsErr("1.txt", 5, errcode.NewMsgNumberOfParameNoMatch(2, 1, "int"), errcode.TManyParame) && !err.IsErr("1.txt", 6, errcode.NewMsgNumberOfParameNoMatch(0, 1, "int"), errcode.InsuParame) && !err.IsErr("1.txt", 7, errcode.NewMsgUnknownSymbol("f"), errcode.UnknownSymbol) {
			panic("a17\n" + err.String())
		}
	}, 4, "a17")
	a18 := newargs([]string{"package main", "func main(){", "printf()", "printf(1)", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 3, nil, errcode.InsuParame) && !err.IsErr("1.txt", 4, nil, errcode.FirstParameOfPrinfASting) {
			panic("a18\n" + err.String())
		}
	}, 2, "a18")
	a19 := newargs([]string{"package main", "func main(){", "add(1,2,3)", "add(1)", "}", "func add(a int,b int){", "return a+b", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 3, errcode.NewMsgNumberOfParameNoMatch(3, 2, "main__add"), errcode.TManyParame) && !err.IsErr("1.txt", 4, errcode.NewMsgNumberOfParameNoMatch(1, 2, "main__add"), errcode.InsuParame) {
			panic("a19\n" + err.String())
		}
	}, 2, "a19")
	a20 := newargs([]string{"package main", "func main(){", "var i int", "add(1.0,&i)", "add(1,&i)", "add(1,nil)", "malloc(1)", "}", "func add(a int,b &int){", "return a+@b", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 4, errcode.NewMsgCallTypeIsNotEqual("int", "float", 1), errcode.TypeIsNotEqual) && !err.IsErr("1.txt", 7, errcode.NewMsgUnknownSymbol("1"), errcode.UnknownType) {
			panic("a20\n" + err.String())
		}
	}, 2, "a20")
	a21 := newargs([]string{"package main", "func main(){", "var s str", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 3, errcode.NewMsgUnknownSymbol("str"), errcode.UnknownType) {
			panic("a21\n" + err.String())
		}
	}, 1, "a21")
	a22 := newargs([]string{"package main", "func main(){", "struct str{", "ptr &int", "len int", "next &str", "}", "var s str", "s.len=1", "s.ln=1", "var i int", "i.f=1", "var p1 &int=&s.len", "var p2 &float=&s.len", "s.next=&s", "s.len=s.next.len", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 10, errcode.NewMsgSelectorLNoSelectorR("s", "ln"), errcode.SelectorLNoSelectorR) && !err.IsErr("1.txt", 12, errcode.NewMsgSymbol("i"), errcode.NoOSelectorL) && !err.IsErr("1.txt", 14, errcode.NewMsgAssignTypeIsNotEqual("&float", "&int"), errcode.TypeIsNotEqual) {
			panic("a22\n" + err.String())
		}
	}, 3, "a22")
	a23 := newargs([]string{"package main", "func main(){", "var s int", "s(1)", "s=value(s)", "}", "func value(i int){", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 5, errcode.NewMsgSymbol("value"), errcode.FuncNoRetValue) && !err.IsErr("1.txt", 4, errcode.NewMsgSymbol("s"), errcode.CallNoFunc) {
			panic("a23\n" + err.String())
		}
	}, 2, "a23")
	a24 := newargs([]string{"package main", "func main(){", "var a int", "a++", "var b bool", "b++", "var s string", "s++", "const c int=1", "c--", "var e &int=&a", "e++", "for ;;e++{", "}", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 6, nil, errcode.BoolOpOnlyCmpOrLogic) && !err.IsErr("1.txt", 8, nil, errcode.StringNoSelfOpStmt) && !err.IsErr("1.txt", 10, errcode.NewMsgAssignToConst("c"), errcode.ASSIGNErr) && !err.IsErr("1.txt", 12, nil, errcode.PtrOpOnlyCmp) && !err.IsErr("1.txt", 13, nil, errcode.PtrOpOnlyCmp) {
			panic("a24\n" + err.String())
		}
	}, 5, "a24")
	a25 := newargs([]string{"package main", "func init(){", "}", "func ginit(){", "}", "func gmain(){", "}", "func main(){", "init()", "main()", "ginit()", "gmain()", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 9, nil, errcode.ProhibitCallInitFunc) && !err.IsErr("1.txt", 10, nil, errcode.ProhibitCallMainFunc) {
			panic("a25\n" + err.String())
		}
	}, 1, "a25") //这里预期一次产生两个错误，由于是在ast时产生的，不会产生2次错误，所以填了1
	a26 := newargs([]string{"package main", "func main(){", "var i &int=malloc(int)", "free(i)", "printf(\"%d%d%d\",@i,1,2)", "mallocSize(i)", "unsafe__Convert(i,float)", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 6, nil, errcode.ParameForMallocSizeMustInt) && !err.IsErr("1.txt", 7, nil, errcode.SecondParameOfUnsafeConvertAPtrType) {
			panic("a26\n" + err.String())
		}
	}, 2, "a26")
	a27 := newargs([]string{"package main", "var i int=2", "func main(){", "switch i{", "case 1:", "i=2", "case 2.0:", "}", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 7, errcode.NewMsgSwitchAndCaseTypNoEqual("int", "float"), errcode.TypeIsNotEqual) {
			panic("a27\n" + err.String())
		}
	}, 1, "a27")
	a28 := newargs([]string{"package main", "struct s{", "len int", "}", "method setint(ptr &s ,l int){", "ptr.len=l", "}", "func a(a1 int){", "a1=1", "}", "func main(){", "}"}, func(err errcode.ErrInfo) {
		panic("a28\n" + err.String())
	}, 0, "a28")
	a29 := newargs([]string{"package main", "struct s{", "len int", "len int", "}", "}", "func main(){", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 2, errcode.NewMsgSymbol("len"), errcode.FieldDupName) {
			panic("a29\n" + err.String())
		}
	}, 1, "a29")
	a30 := newargs([]string{"package main", "func main(){", "var a [2]int", "a[n]=9", "a[0]=a[n]", "var f int", "f[2]=1", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 4, errcode.NewMsgUnknownSymbol("n"), errcode.UnknownSymbol) && !err.IsErr("1.txt", 5, errcode.NewMsgUnknownSymbol("n"), errcode.UnknownSymbol) && !err.IsErr("1.txt", 7, nil, errcode.IndexExprElemTypeErr) {
			panic("a30\n" + err.String())
		}
	}, 3, "a30")
	a31 := newargs([]string{"package main", "struct s{", "p int", "}", "var b s", "var c int", "func main(){", "var a s", "b=@a", "c=@(a.p)", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 9, nil, errcode.OnlyTypePtrCanDeref) && !err.IsErr("1.txt", 10, nil, errcode.OnlyTypePtrCanDeref) {
			panic("a31\n" + err.String())
		}
	}, 2, "a31")
	a32 := newargs([]string{"package main"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", -1, nil, errcode.MainPackageMustHaveMainFunc) {
			panic("a32\n" + err.String())
		}
	}, 1, "a32")
	a33 := newargs([]string{"package dep", "func main(){", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", -1, nil, errcode.NoMainPackageMustHaveNoMainFunc) && !err.IsErr("1.txt", 1, errcode.NewMsgPackageNameNoEqualDirName("dep", "1"), errcode.PackageNameNoEqualDirName) {
			panic("a33\n" + err.String())
		}
	}, 2, "a33")
	tests := []struct {
		name string
		args args
	}{
		{"int <- int,int <- float 正常赋值和目的操作数与源操作数类型不相等", a1},
		{"int <- int 多运算符表达式正常赋值", a3},
		{"int <- float 多运算符表达式,左右源操作数类型不相等", a4},
		{"int <- float 赋值,目的操作数与源操作数类型不相等", a5},
		{"变量b使用(赋值)在声明之前", a6},
		{"变量b使用(参加运算)在声明之前", a7},
		{"if语句布尔表达式正确与错误", a8},
		{"else if语句布尔表达式正确与错误", a9},
		{"for语句正确与错误", a10},
		{"赋值中未知的符号", a11},
		{"对布尔类型进行非判等运算", a12},
		{"声明int返回bool", a13},
		{"不存在的标签", a14},
		{"break,continue语句不在for代码块中", a15},
		{"常量赋值及使用在声明之前", a16},
		{"类型转换错误", a17},
		{"参数不足和printf第一个参数不是字符串", a18},
		{"参数数量错误", a19},
		{"参数类型错误", a20},
		{"未知的类型", a21},
		{"选择器正确与错误", a22},
		{"调用错误", a23},
		{"自操作语句", a24},
		{"调用禁止调用的函数", a25},
		{"与内置函数有关", a26},
		{"switch语句相关", a27},
		{"函数参数视为局部变量", a28},
		{"字段重名", a29},
		{"索引表达式", a30},
		{"错误解引用", a31},
		{"main包没有main函数", a32},
		{"非main包有main函数", a33},
	}
	wg := utils.NewWaitGroup(Thread2)
	for _, ttf := range tests {
		tt := ttf
		wg.Go(func() {
			defer wg.Done()
			check.CheckTree(tt.args.tree, tt.args.errctx, false)
			check.CheckTree(tt.args.tree, tt.args.errctx, false)
			tt.args.cancel()
		})
	}
	wg.Wait()
}

// 改编自ast包的TestCheckSyntaxErr
func TestCheckSyntaxErr() {
	type args struct {
		str    []string
		errctx *errcode.ErrCtx
		cancel context.CancelFunc
	}
	newargs := func(str []string, errf func(errcode.ErrInfo), num int, name string) args {
		actx := errcode.NewErrCtx()
		astdctx, acancel := context.WithCancel(context.Background())
		i := 0
		num = num * 2 //因为会检查两次，所以乘2
		s := make(chan struct{})
		go actx.Handle(astdctx, func(info errcode.ErrInfo) { //每收到一个错误，i++
			i++
			errf(info)
		}, func() { s <- struct{}{} })
		go func() {
			<-astdctx.Done()
			if num != 0 { //如果预期发生错误数量大于0，等待处理所有错误完毕
				<-s
			}
			if num != i { //如果预期发生错误数量不等于实际发生错误数量
				panic(fmt.Errorf("%s 应该有%d个错误，实际有%d个错误", name, num, i))
			}
		}()
		ret := args{str: str, errctx: actx, cancel: acancel}
		return ret
	}
	a1 := newargs([]string{"", "func main(){", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 1, nil, errcode.PackageDeclMustFirstLine) {
			panic("a1\n" + err.String())
		}
	}, 1, "a1")
	a2 := newargs([]string{"package main", "func main(){", "var ", "var a", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 3, nil, errcode.VARErr, errcode.NoName) && !err.IsErr("1.txt", 4, nil, errcode.VARErr, errcode.NoType) {
			panic("a2\n" + err.String())
		}
	}, 2, "a2")
	a3 := newargs([]string{"package main", "func main(){", "const ", "const a", "const a int", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 3, nil, errcode.ConstErr, errcode.NoName) && !err.IsErr("1.txt", 4, nil, errcode.ConstErr, errcode.NoType) && !err.IsErr("1.txt", 5, nil, errcode.ConstErr, errcode.NoConstInit) {
			panic("a3\n" + err.String())
		}
	}, 3, "a3")
	a4 := newargs([]string{"package main", "func main(){", "1:", "t : t", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 3, nil, errcode.LabelErr, errcode.NoName) && !err.IsErr("1.txt", 4, nil, errcode.LabelErr, errcode.OPbug) {
			panic("a4\n" + err.String())
		}
	}, 2, "a4")
	a5 := newargs([]string{"package main", "func main(){", "goto ", "goto 1", "goto e e", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 3, nil, errcode.GotoErr, errcode.NoLabel) && !err.IsErr("1.txt", 4, nil, errcode.GotoErr, errcode.NoLabel) && !err.IsErr("1.txt", 5, nil, errcode.GotoErr, errcode.OPbug) {
			panic("a5\n" + err.String())
		}
	}, 3, "a5")
	a6 := newargs([]string{"package main", "func {", "}", "func a {", "}", "func a(o int)", "}", "func a o int){", "}", "func a(o int{", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 2, nil, errcode.FUNCErr, errcode.NoName) && !err.IsErr("1.txt", 4, nil, errcode.FUNCErr, errcode.EmptyLPAREN) && !err.IsErr("1.txt", 6, nil, errcode.FUNCErr, errcode.LbraceEndOfLine) && !err.IsErr("1.txt", 8, nil, errcode.FUNCErr, errcode.EmptyLPAREN) && !err.IsErr("1.txt", 10, nil, errcode.FUNCErr, errcode.EmptyRPAREN) {
			panic("a6\n" + err.String())
		}
	}, 5, "a6")
	a7 := newargs([]string{"package main", "func s[i](){", "}", "func s[i 3](){", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 2, nil, errcode.TypeParameSyntaxErr) && !err.IsErr("1.txt", 4, nil, errcode.FUNCErr, errcode.UnknownType) && !err.IsErr("1.txt", 4, nil, errcode.TypeParamrConstraintErr) {
			panic("a7\n" + err.String())
		}
	}, 3, "a7")
	a8 := newargs([]string{"func main(){", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 1, nil, errcode.PackageDeclMustFirstLine) {
			panic("a8\n" + err.String())
		}
	}, 1, "a8")
	a9 := newargs([]string{"package main", "func main(){", "}", "enum {", "}", "enum j{", "}", "enum k{", "", "1", "q", "q", "f g", "}", "enum s{"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 4, nil, errcode.EnumErr, errcode.NoName) && !err.IsErr("1.txt", 7, nil, errcode.EnumErr, errcode.EmptyEnum) && !err.IsErr("1.txt", 10, nil, errcode.EnumErr, errcode.EnumValueShouldBeASymbol) && !err.IsErr("1.txt", 12, errcode.NewMsgSymbol("q"), errcode.EnumErr, errcode.EnumValueDup) && !err.IsErr("1.txt", 13, nil, errcode.EnumErr, errcode.EnumValueShouldHaveOneOnEachLine) && !err.IsErr("1.txt", 15, nil, errcode.EnumErr, errcode.EmptyRbrace) {
			panic("a9\n" + err.String())
		}
	}, 6, "a9")
	a10 := newargs([]string{"package main", "func main(){", "}", "struct {", "struct a{", "", "9 int", "s []p", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 4, nil, errcode.StructErr, errcode.NoName) && !err.IsErr("1.txt", 5, nil, errcode.StructErr, errcode.LbraceEndOfLine) && !err.IsErr("1.txt", 7, nil, errcode.StructErr, errcode.FieldErr, errcode.NoIdent) && !err.IsErr("1.txt", 8, nil, errcode.StructErr, errcode.UnknownType) {
			panic("a10\n" + err.String())
		}
	}, 3, "a10")
	a11 := newargs([]string{"package main", "func main(){", "if ", "if {", "}", "if a.{", "}", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 3, nil, errcode.IfErr, errcode.LbraceEndOfLine) && !err.IsErr("1.txt", 4, nil, errcode.IfErr, errcode.NoBoolExpr) && !err.IsErr("1.txt", 6, nil, errcode.LeftOrRightValueSelectorEmpty) {
			panic("a11\n" + err.String())
		}
	}, 3, "a11")
	a12 := newargs([]string{"package main", "func main(){", "else if ", "else if a.{", "}", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 3, nil, errcode.ElseErr, errcode.LbraceEndOfLine) && !err.IsErr("1.txt", 4, nil, errcode.LeftOrRightValueSelectorEmpty) {
			panic("a12\n" + err.String())
		}
	}, 2, "a12")
	a13 := newargs([]string{"package main", "func main(){", "for ", "for {", "}", "for ;{", "}", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 3, nil, errcode.ForErr, errcode.LbraceEndOfLine) && !err.IsErr("1.txt", 4, errcode.NewMsgNumberOfSemicolons(0), errcode.ForErr) && !err.IsErr("1.txt", 6, errcode.NewMsgNumberOfSemicolons(1), errcode.ForErr) {
			panic("a13\n" + err.String())
		}
	}, 3, "a13")
	a14 := newargs([]string{"package main", "func main(){", "main()", "init()", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 3, nil, errcode.ProhibitCallMainFunc) && !err.IsErr("1.txt", 4, nil, errcode.ProhibitCallInitFunc) {
			panic("a14\n" + err.String())
		}
	}, 2, "a14")
	a15 := newargs([]string{"package main", "func main(){", "switch {", "}", "switch", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 3, nil, errcode.SwitchErr, errcode.NoExpr) && !err.IsErr("1.txt", 5, nil, errcode.SwitchErr, errcode.LbraceEndOfLine) {
			panic("a15\n" + err.String())
		}
	}, 2, "a15")
	a16 := newargs([]string{"package main", "func main(){", "switch 1{", "case", "case:", "}", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 4, nil, errcode.CaseErr, errcode.ExpectColonAtEnd) && !err.IsErr("1.txt", 5, nil, errcode.CaseErr, errcode.NoExpr) {
			panic("a16\n" + err.String())
		}
	}, 2, "a16")
	a17 := newargs([]string{"package main", "func main(){", "switch 1{", "default", "default 6:", "}", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 4, nil, errcode.DefaultErr, errcode.ExpectColonAtEnd) && !err.IsErr("1.txt", 5, nil, errcode.DefaultErr, errcode.OPbug) {
			panic("a17\n" + err.String())
		}
	}, 2, "a17")
	a18 := newargs([]string{"package main", "func main(){", "}", "method", "method {", "method a {", "method a ({", "method a (){", "method a (i int){", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 4, nil, errcode.MethodErr, errcode.LbraceEndOfLine) && !err.IsErr("1.txt", 5, nil, errcode.MethodErr, errcode.NoName) && !err.IsErr("1.txt", 6, nil, errcode.MethodErr, errcode.EmptyLPAREN) && !err.IsErr("1.txt", 7, nil, errcode.MethodErr, errcode.EmptyRPAREN) && !err.IsErr("1.txt", 8, nil, errcode.MethodErr, errcode.FirstParameTypeOfMethodACustomType) && !err.IsErr("1.txt", 9, nil, errcode.MethodErr, errcode.FirstParameTypeOfMethodACustomType) {
			panic("a18\n" + err.String())
		}
	}, 6, "a18")
	a19 := newargs([]string{"package main", "import {", "}", "func main(){", "import", "}", "import ", "import {", "1", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 5, nil, errcode.ImportErr, errcode.ImportShouldBeOutSideOfTheFunc) && !err.IsErr("1.txt", 7, nil, errcode.ImportErr, errcode.LbraceEndOfLine) && !err.IsErr("1.txt", 9, nil, errcode.ImportErr, errcode.ImportPathMustString) {
			panic("a19\n" + err.String())
		}
	}, 3, "a19")
	a20 := newargs([]string{"package main", "import {"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 2, nil, errcode.ImportErr, errcode.EmptyRbrace) {
			panic("a20\n" + err.String())
		}
	}, 1, "a20")
	a21 := newargs([]string{"package main", "func main(){", "switch i {", "case 100 :", "if i > 50 {", "i++", "}", "if i > 90", " {", "i++", "printf(i)", "default :", "}", "}", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 4, errcode.NewMsgBracesNoEqual(2, 1), errcode.CaseErr) {
			panic("a21\n" + err.String())
		}
	}, 1, "a21")
	a22 := newargs([]string{"package main", "func main(){", "switch i {", "default :", "if i > 50 {", "i++", "}", "if i > 90", " {", "i++", "printf(i)", "case 78 :", "}", "}", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 4, errcode.NewMsgBracesNoEqual(2, 1), errcode.DefaultErr) {
			panic("a22\n" + err.String())
		}
	}, 1, "a22")
	a23 := newargs([]string{"package ", "package unsafe", "package f f", "package main"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 1, nil, errcode.PackageDeclMustName) && !err.IsErr("1.txt", 2, nil, errcode.PackageNameCannotBeEqualUnsafe) && !err.IsErr("1.txt", 3, nil, errcode.PackageDeclOnlyName) && !err.IsErr("1.txt", 4, nil, errcode.PackageDeclMustFirstLine) {
			panic("a23\n" + err.String())
		}
	}, 4, "a23")
	a24 := newargs([]string{"package main", "func main(){", "autofree {", "}", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 3, nil, errcode.AutoFreeErr, errcode.NoExpr) {
			panic("a24\n" + err.String())
		}
	}, 1, "a24")
	a25 := newargs([]string{"package main", "struct s[T any]{", "}", "func main(){", "var b s[]", "var g s[1]", "var f s[int,int] ", "}"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 5, nil, errcode.VARErr, errcode.GenericInstantiationEmpty) && !err.IsErr("1.txt", 6, nil, errcode.VARErr, errcode.UnknownType) && !err.IsErr("1.txt", 7, nil, errcode.UnequlaNumOfGen) {
			panic("a25\n" + err.String())
		}
	}, 3, "a25")
	a26 := newargs([]string{"package main", "/*", "*/", "/*"}, func(err errcode.ErrInfo) {
		if !err.IsErr("1.txt", 4, nil, errcode.MLCNoEnd) {
			panic("a26\n" + err.String())
		}
	}, 1, "a26")
	tests := []struct {
		name string
		args args
	}{
		{"第一行空，没有包声明", a1},
		{"变量错误", a2},
		{"常量错误", a3},
		{"标签错误", a4},
		{"goto错误", a5},
		{"函数错误", a6},
		{"泛型错误", a7},
		{"第一行非空，没有包声明", a8},
		{"枚举错误", a9},
		{"结构体错误", a10},
		{"if错误", a11},
		{"else if错误", a12},
		{"for错误", a13},
		{"禁止调用函数错误", a14},
		{"switch错误", a15},
		{"case错误", a16},
		{"default错误", a17},
		{"方法错误", a18},
		{"import错误", a19},
		{"import右大括号缺失", a20},
		{"case中的代码块错误", a21},
		{"default中的代码块错误", a22},
		{"package错误", a23},
		{"自动释放块错误", a24},
		{"泛型错误", a25},
		{"多行注释错误", a26},
	}
	wg := utils.NewWaitGroup(Thread2)
	for _, ttf := range tests {
		tt := ttf
		wg.Go(func() {
			defer wg.Done()
			ParserStr(tt.args.str, tt.args.errctx)
			ParserStr(tt.args.str, tt.args.errctx)
			tt.args.cancel()
		})
	}
	wg.Wait()
}
